package com.hexinfo.dmpro.admin.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.hexinfo.dmpro.common.annotation.AnonymousAccess;
import com.hexinfo.dmpro.common.annotation.Log;
import com.hexinfo.dmpro.common.cache.Cache;
import com.hexinfo.dmpro.common.cache.CacheFactory;
import com.hexinfo.dmpro.admin.config.SecurityProperties;
import com.hexinfo.dmpro.common.exception.BadRequestException;
import com.hexinfo.dmpro.admin.model.vo.LoginVo;
import com.hexinfo.dmpro.admin.security.TokenProvider;
import com.hexinfo.dmpro.admin.security.vo.AuthUser;
import com.hexinfo.dmpro.admin.security.vo.JwtUser;
import com.hexinfo.dmpro.common.utils.SecurityUtils;
import com.hexinfo.dmpro.common.utils.StringUtils;
import com.wf.captcha.ArithmeticCaptcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Zheng Jie
 * @date 2018-11-23
 * 授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@Api(tags = "系统：系统授权接口")
public class AuthController {

    /**
     * 登录图形验证码有效时间/分钟
     */
    @Value("${loginCode.expiration}")
    private Long expiration;

    /**
     * 密码加密传输，前端公钥加密，后端私钥解密
     */
    @Value("${rsa.private_key}")
    private String privateKey;

    @Value("${single.login:false}")
    private Boolean singleLogin;


    private static final String REFRESHTOKEN_KEY = "refreshToken";

    private final SecurityProperties properties;

    private final Cache cache;

    private final UserDetailsService userDetailsService;

    private final TokenProvider tokenProvider;

    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    public AuthController(SecurityProperties properties, CacheFactory cacheFactory,
                          UserDetailsService userDetailsService,
                          TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
        this.properties = properties;
        this.cache = cacheFactory.createCache();
        this.userDetailsService = userDetailsService;
        this.tokenProvider = tokenProvider;
        this.authenticationManagerBuilder = authenticationManagerBuilder;

    }

    @Log("用户登录")
    @ApiOperation("登录授权")
    @AnonymousAccess
    @PostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUser authUser, HttpServletRequest request) {
        // 密码解密
        RSA rsa = new RSA(privateKey, null);
        String password = new String(rsa.decrypt(authUser.getPassword(), KeyType.PrivateKey));
        // 查询验证码
        String code = (String) cache.get(authUser.getUuid());
        // 清除验证码
        cache.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);

        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);
        // 生成刷新令牌
        String refreshToken = tokenProvider.createRefreshToken(authentication);
        final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<>(3);
        authInfo.put("token", properties.getTokenStartWith() + token);
        authInfo.put("refreshToken", properties.getRefreshTokenStartWith() + refreshToken);
        authInfo.put("user", jwtUser);
        return ResponseEntity.ok(authInfo);
    }

    @Log("免验证码用户登录")
    @ApiOperation("登录授权")
    @AnonymousAccess
    @PostMapping(value = "/noCodeLogin")
    public ResponseEntity<Object> noCodeLogin(@Validated @RequestBody AuthUser authUser, HttpServletRequest request) {
        // 密码解密
        RSA rsa = new RSA(privateKey, null);
        String password = new String(rsa.decrypt(authUser.getPassword(), KeyType.PrivateKey));
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);

        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);
        // 生成刷新令牌
        String refreshToken = tokenProvider.createRefreshToken(authentication);
        final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<>(3);
        authInfo.put("token", properties.getTokenStartWith() + token);
        authInfo.put("refreshToken", properties.getRefreshTokenStartWith() + refreshToken);
        authInfo.put("user", jwtUser);
        return ResponseEntity.ok(authInfo);
    }

    @Log("用户token登录")
    @ApiOperation("token登录授权")
    @AnonymousAccess
    @PostMapping(value = "/loginByToken")
    public ResponseEntity<Object> loginByToken(@RequestBody LoginVo loginVo, HttpServletRequest request) {
        String token = loginVo.getToken();
        Authentication authentication = tokenProvider.getAuthentication(token);
        User user = (User) authentication.getPrincipal();
        JwtUser jwtUser = (JwtUser) userDetailsService.loadUserByUsername(user.getUsername());
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<>(2);
        authInfo.put("token", properties.getTokenStartWith() + token);
        authInfo.put("user", jwtUser);
        return ResponseEntity.ok(authInfo);
    }

    @ApiOperation("刷新Token")
    @AnonymousAccess
    @PostMapping(value = "/refreshToken")
    public ResponseEntity<Object> refreshToken(@RequestBody Map<String, Object> params, HttpServletRequest request) {
        String tokenValue = params.get(REFRESHTOKEN_KEY).toString();
        Map<String, Object> authInfo = new HashMap<>(2);
        // 判断refreshToken是否过期，false 表示未过期
        if (StringUtils.isNotBlank(tokenValue)
                && !tokenProvider.isExpiration(tokenValue.substring(7))) {
            String newToken = tokenProvider.createToken(tokenValue.substring(7));
            String newRefreshToken = tokenProvider.createRefreshToken(tokenValue.substring(7));
            String username = tokenProvider.getClaims(tokenValue.substring(7)).getSubject();
            JwtUser jwtUser = (JwtUser) userDetailsService.loadUserByUsername(username);
            authInfo.put("token", properties.getTokenStartWith() + newToken);
            authInfo.put("refreshToken", properties.getRefreshTokenStartWith() + newRefreshToken);
        }
        return ResponseEntity.ok(authInfo);
    }

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public ResponseEntity<Object> getUserInfo() {
        JwtUser jwtUser = (JwtUser) userDetailsService.loadUserByUsername(SecurityUtils.getUsername());
        return ResponseEntity.ok(jwtUser);
    }

    @AnonymousAccess
    @ApiOperation("获取验证码")
    @GetMapping(value = "/code")
    public ResponseEntity<Object> getCode() {
        // 算术类型 https://gitee.com/whvse/EasyCaptcha
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        // 几位数运算，默认是两位
        captcha.setLen(2);
        // 获取运算的结果
        String result = captcha.text();
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
        // 保存
        cache.setTimeout(uuid, result, expiration * 60000);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<>(2);
        imgResult.put("img", captcha.toBase64());
        imgResult.put("uuid", uuid);
        return ResponseEntity.ok(imgResult);
    }

    @ApiOperation("退出登录")
    @AnonymousAccess
    @DeleteMapping(value = "/logout")
    public ResponseEntity<Object> logout(HttpServletRequest request) {
        cache.del(SecurityUtils.getUsername());
        return new ResponseEntity<>(HttpStatus.OK);
    }
}
